package app

import (
	"encoding/json"
	"errors"

	"gitee.com/leicc/go-imsrv-app/app/logic"
	"gitee.com/leicc/go-orm"
	"gitee.com/leicc/go-core"
	"gitee.com/leicc/go-orm/log"
	"gitee.com/leicc/go-app-core/models/im"
)

const (
	_			= iota
	SIGNIN 	//连接之后加入首次握手协议 连接句柄和账号做捆绑
	JOINCHAN//请求加入或者离开房间
	SNDC2C	//发送C2C私聊
	SNDCHAN	//发送频道群聊天
	SNDPING	//发送消息Ping
)

type WSPacketSt struct {
	OpCmd 	int8	`json:"opcmd"`
	SeqNo 	string	`json:"seqno"`
	Packet 	string	`json:"packet"`
}

type WsImSrvMessageSt struct {
	UserID   int64 	`json:"userid,string"`
	ToUserId int64 	`json:"touserid,string"`
	TargetId int64 	`json:"targetid,string"`
	Type     int16  `json:"type"`
}

func init() {//默认注册im信息处理逻辑
	RegisterPacketHandler(doPacketHandlerProc)
}

//创建一个数据包裹业务处理逻辑
func (w *WSPacketSt) doPacket(userid int64, msg orm.SqlMap) ([]byte, error) {
	sorm := im.NewImUser().SetModTable(userid)
	user := logic.UserSt{} //获取用户的基础信息
	err  :=  sorm.GetItem(func(st *orm.QuerySt) string {
		st.Where("userid", userid).Where("appid", logic.GConfig.Appid)
		return st.GetWheres()
	}, "userid,nick,avatar,gender").ToStruct(&user)
	if err != nil || user.UserId < 1 {
		log.Write(log.ERROR, err)
		return nil, err
	}
	msg["user"] = user //附加发送者用户信息
	if mPacket, err := json.Marshal(msg); err != nil {//出错的处理逻辑
		log.Write(log.ERROR, err)
		return nil, err
	} else {
		message, err := json.Marshal(orm.SqlMap{"opcmd":w.OpCmd, "seqno":w.SeqNo, "packet":string(mPacket)})
		if err != nil {//出错的处理逻辑
			log.Write(log.ERROR, err)
			return nil, err
		}
		return message, nil
	}
}

//客户端客户端的业务逻辑数据信息
func doPacketHandlerProc(c *Client, msgType int, message []byte) {
	var err *core.HttpError = nil
	packet := &WSPacketSt{} //生成一个包数据
	defer func() {//业务逻辑恐慌拦截让业务更健壮
		if e := recover(); e != nil {
			cbStack := orm.RuntimeStack(3)
			log.Write(log.ERROR, e, string(cbStack))
			err = &core.HttpError{Code: 500, Msg: "内部服务异常,无法操作"}
		}
		doCallBackClient(c, packet, err)
	}()
	log.Write(log.INFO, "message type{", msgType, "}", string(message))
	if e := json.Unmarshal(message, packet); e != nil {
		log.Write(log.ERROR, string(message), e)
		return
	}
	switch packet.OpCmd {
	case SIGNIN: //登录逻辑的处理
		err = doSignIn(c, packet)
	case JOINCHAN: //加入离开聊天室
		err = doJoinChan(c, packet)
	case SNDC2C: //发送私人聊天
		err = doSndC2C(c, packet, message)
	case SNDCHAN: //聊天室群广播
		err = doSndChan(c, packet, message)
	case SNDPING: //发送Ping检测心跳
		err = nil
	default:
		log.Write(log.DEBUG, "packet opcmd 404"+string(message))
	}
}

//持久化存储到mysql当中
func doPersistStore(userid int64, packet string) error {
	msgSt := logic.ArgsOfflineMessages{}
	err := json.Unmarshal([]byte(packet), &msgSt)
	if err != nil || len(msgSt.Messages) < 1 {
		log.Write(log.ERROR, "数据持久化解析异常", packet, err)
		return errors.New("请求数据包解析异常")
	}
	msgSt.UserId = userid
	logic.GStore.Send(msgSt) //发送到管道异步进行持久化处理
	return nil
}

//统一的错误输出控制逻辑
func doCallBackClient(c *Client, packet *WSPacketSt, err *core.HttpError) {
	data := map[string]interface{}{"code":0, "msg":"OK", "opcmd":packet.OpCmd, "seqno":packet.SeqNo}
	if err != nil { //错误信息不为空的情况
		data["code"], data["msg"] = err.Code, err.Msg
	}
	if byteStr, e := json.Marshal(data); e != nil {
		log.Write(log.ERROR, e.Error())
	} else {//发送json数据到前端
		c.Send(byteStr)
	}
}

//发送频道聊天数据信息
func doSndChan(c *Client, packet *WSPacketSt, message []byte) *core.HttpError {
	args := &WsImSrvMessageSt{}
	if err := json.Unmarshal([]byte(packet.Packet), &args); err != nil {
		return &core.HttpError{Code:1001, Msg: "解析json包异常", Debug: err.Error()}
	}
	nsize, userId := 0, c.State.GetUserId(c)
	if userId < 1 || userId != args.UserID {//未登录的情况
		return &core.HttpError{Code:1002, Msg: "不允许自己给自己发消息哟"}
	}
	if err := doPersistStore(userId, packet.Packet); err != nil {
		return &core.HttpError{Code:1003, Msg: "内部错误,信息存储异常"}
	}
	if logic.GMQueue.IsSupportMQ() { //将频道和世界广播消息插入消息队列当中
		qmsg := &logic.QueueMessageSt{Message: message, Type: args.Type, UserId: userId, ToId: args.ToUserId}
		logic.GMQueue.IntoAllMQueue(qmsg) //将消息插入消息对了当中
	}
	if args.Type == 1 {//频道聊天 开启独立线程推送
		nsize = c.State.PushMessageToChannel(userId, args.ToUserId, message)
		log.Write(log.INFO, "频道{",args.ToUserId,"}推送{",nsize,"}条消息")
	} else {//世界频道聊天
		nsize = c.State.PushMessageToAll(c, message)
		log.Write(log.INFO, "世界频道推送{",nsize,"}条消息")
	}
	return nil
}

//发送私人聊天数据信息
func doSndC2C(c *Client, packet *WSPacketSt, message []byte) *core.HttpError {
	args := &WsImSrvMessageSt{}
	if err := json.Unmarshal([]byte(packet.Packet), &args); err != nil {
		return &core.HttpError{Code:1001, Msg: "解析json包异常", Debug: err.Error()}
	}
	userId := c.State.GetUserId(c)
	if userId < 1 || userId != args.UserID {//未登录的情况
		return &core.HttpError{Code:1002, Msg: "不允许自己给自己发消息哟"}
	}
	if err := doPersistStore(userId, packet.Packet); err != nil {
		return &core.HttpError{Code:1003, Msg: "内部错误,信息存储异常"}
	}
	if logic.RState.IsOnline(logic.GConfig.Appid, args.ToUserId) {
		mqTopic := logic.RState.MQueue(logic.GConfig.Appid, args.ToUserId)
		log.Write(log.INFO, mqTopic, "<==MQ队列==>", logic.GConfig.MQueue)
		if mqTopic == logic.GConfig.MQueue { //在线且隶属同一个服务器的情况 直达
			nsize := c.State.PushMessageToUser(args.ToUserId, message)
			log.Write(log.INFO,"C2C给用户{",args.ToUserId,"}推送{",nsize,"}条消息")
		} else if logic.GMQueue.IsSupportMQ() { //初始化消息然后放入队列当中
			qmsg := &logic.QueueMessageSt{Message: message, UserId: userId, ToId: args.ToUserId, Type: 0}
			logic.GMQueue.IntoMQueue(mqTopic, qmsg) //递交给消息队列转发处理逻辑
		}
	} else {
		log.Write(log.INFO, "对方离线,消息直接入库持久化...")
	}
	return nil
}

//加入房间的信息操作，这里需要广播消息
func doJoinChan(c *Client, packet *WSPacketSt) *core.HttpError {
	args   := struct {
		Chnid int64  `json:"chnid,string"`
		OpType int8  `json:"optype"`
	}{}
	if err := json.Unmarshal([]byte(packet.Packet), &args); err != nil {
		return &core.HttpError{Code:1001, Msg: "解析json包异常", Debug: err.Error()}
	}
	userId := c.State.GetUserId(c)
	if userId < 1 {//未登录的情况
		return &core.HttpError{Code:1002, Msg: "未登录,无法加入房间"}
	}
	logic.RState.JoinQuitChan(logic.GConfig.Appid, userId, args.Chnid, args.OpType)
	//消息广播处理逻辑 --通知加入房间或者离开房间
	data := orm.SqlMap{"optype":args.OpType, "chnid":args.Chnid}
	if message, err := packet.doPacket(userId, data); err != nil {
		return &core.HttpError{Code:1003, Msg: "请求业务数据包解析异常", Debug: err.Error()}
	} else {                            //发送消息通知广播处理逻辑
		if logic.GMQueue.IsSupportMQ() { //将频道和世界广播消息插入消息队列当中
			qmsg := &logic.QueueMessageSt{Message: message, Type: 1, UserId: userId, ToId: args.Chnid}
			logic.GMQueue.IntoAllMQueue(qmsg) //将消息插入消息对了当中
		}
		c.State.PushMessageToChannel(userId, args.Chnid, message)
	}
	return nil
}

//连接握手协议 完成账号->句柄的映射绑定
func doSignIn(c *Client, packet *WSPacketSt) *core.HttpError {
	args := struct {
		Token string `json:"vkey"`
		UserId int64 `json:"userid,string"`
	}{}
	if err := json.Unmarshal([]byte(packet.Packet), &args); err != nil {
		return &core.HttpError{Code:1001, Msg: "解析json包异常", Debug: err.Error()}
	}
	if !logic.RState.Token(logic.GConfig.Appid, args.UserId, args.Token) {
		return &core.HttpError{Code:1005, Msg: "请求的token异常,无法登录"}
	}
	c.State.Register(args.UserId, c) //注册到状态管理中心
	return nil
}


